"""
业务日志文件 | 业务层
"""

import os
import re

from biz.base_biz import GetterBase
from components.logger import (
    LOG_ELEMENT_SEP,
    LOG_SEP,
)
from components.pagination import Paginator
from config import settings
from mapping import (
    BizLogFileMapping,
    BizLogMapping,
)
from models import (
    PageQuery,
    POPage,
)
from models.other import (
    BizLog,
    BizLogFile,
)
from models.request import (
    BizLogFileListRequest,
    BizLogListRequest,
)
from models.vo import (
    BizLogFileListPage,
    BizLogListPage,
)
from my_typing import *
from utils.converter_utils import TimeConverter
from utils.exception_utils import BusinessError

ListBizLog = list[BizLog]
ListBizLogFile = list[BizLogFile]

BIZ_LOG_FILE_PATH: Path = settings.PATH.LOG_DIR
""" 业务日志文件目录 """


class LogLine(NamedTuple):
    """ 日志每一行的内容 """

    log_line: int
    log_time: str
    log_level: str
    process_id: int
    thread_id: int
    code_path: str
    code_file: str
    code_line: int
    trace_id: int
    message: str


class BizLogFileValidator:
    """ 业务日志文件 | 校验器 """

    LOG_FILE_NAME_PATTERN: str = r"\d{4}-\d{2}-\d{2}\.log"
    """
    日志文件名称正则

    - eg: 2024-09-24.log
    """

    @staticmethod
    def is_valid_file_date(log_file_date: StrOrNone) -> bool:
        """ 日志文件日期参数是否合法 """

        return not (
            log_file_date
            and not re.match(settings.REGEX.DATE_REGEX, log_file_date)
        )

    @classmethod
    def is_valid_log_filename(cls, log_filename: str) -> bool:
        """ 日志文件名称是否合法 """

        return bool(
            log_filename
            and re.match(cls.LOG_FILE_NAME_PATTERN, log_filename)
        )

    @classmethod
    def validate_file_date(cls, log_file_date: StrOrNone) -> None:
        """ 校验日志文件日期参数 """

        if not cls.is_valid_file_date(log_file_date):
            raise BusinessError("日志文件日期参数非法")


class BizLogFileGetter(GetterBase):
    """ 业务日志文件 | 获取器 """

    @staticmethod
    def _get_biz_log_file(
        filename: str,
        include_log_list: bool,
    ) -> BizLogFile:
        """ 获取业务日志文件 """

        file_path: Path = BIZ_LOG_FILE_PATH / filename
        file_size: int = os.path.getsize(file_path)
        log_list: ListBizLog = []

        if (
            include_log_list
            and file_size
        ):
            with open(file_path, "r", encoding="UTF-8") as f:
                content: str = f.read()

            if content:
                line_list: ListStr = content.split(LOG_SEP)

                for idx, line in enumerate(filter(None, line_list), 1):
                    log_line = LogLine(idx, *line.split(LOG_ELEMENT_SEP))
                    log_list.append(
                        BizLog(
                            **{
                                **log_line._asdict(),
                                "message": log_line.message.rstrip("\n"),  # 移除末尾的换行符
                            },
                        )
                    )

        return BizLogFile(
            file_name=filename,
            file_date=TimeConverter.date_str_2_date(filename.split(".")[0]),
            file_size=file_size,
            log_list=log_list,
            log_count=len(log_list),
        )

    @classmethod
    def get_biz_log_file(
        cls,
        include_log_list: bool = False,
        start_time: DateTimeOrNone = None,
        end_time: DateTimeOrNone = None,
    ) -> ListBizLogFile:
        """
        获取业务日志文件

        Args:
            include_log_list: 是否需要包含日志列表
            start_time: 开始时间
            end_time: 结束时间

        Returns:
            业务日志文件列表
        """

        filename_list: ListStr = os.listdir(BIZ_LOG_FILE_PATH)
        valid_filename_list: ListStr = [
            _
            for _ in filename_list
            if BizLogFileValidator.is_valid_log_filename(_)
        ]
        biz_log_file_list: ListBizLogFile = []

        for filename in valid_filename_list:
            file_date: date = TimeConverter.date_str_2_date(filename.split(".")[0])

            if (
                start_time
                and file_date < start_time.date()
            ):
                continue

            if (
                end_time
                and file_date > end_time.date()
            ):
                continue

            biz_log_file_list.append(cls._get_biz_log_file(filename, include_log_list))

        return biz_log_file_list

    @classmethod
    def list(cls, request: BizLogFileListRequest) -> BizLogFileListPage:
        """ 获取业务日志文件列表 """

        page_query: PageQuery = cls.get_list_page_query(request)
        data_list: ListBizLogFile = cls.get_biz_log_file(include_log_list=True)
        pagination = Paginator(page_query, data_list)
        page: POPage[BizLogFile] = pagination.get_page()

        return BizLogFileMapping.po_page_2_vo_page(page)  # noqa: ignore[attr type]

    @staticmethod
    def get_list_query(request: BizLogFileListRequest) -> dict:
        query: dict = {}

        if request.file_date_start:
            query.setdefault("file_date", {}).update({"$gte": request.file_date_start})

        if request.file_date_end:
            query.setdefault("file_date", {}).update({"$lte": request.file_date_end})

        return query


class BizLogGetter(GetterBase):
    """ 业务日志 | 获取器 """

    @classmethod
    def list(cls, request: BizLogListRequest) -> BizLogListPage:
        """ 获取业务日志列表 """

        page_query: PageQuery = cls.get_list_page_query(request)
        data_list: ListBizLog = cls.get_list_from_file(request)
        pagination = Paginator(page_query, data_list)
        page: POPage[BizLog] = pagination.get_page()

        return BizLogMapping.po_page_2_vo_page(page)  # noqa: ignore[attr type]

    @staticmethod
    def get_list_query(request: BizLogListRequest) -> dict:
        query: dict = {}

        eq_fields: tuple[str, str, str, str] = ("log_level", "process_id", "thread_id", "code_line")
        """ 精确查询的字段 """

        regex_fields: tuple[str, str] = ("code_path", "message")
        """ 模糊查询的字段 """

        if request.log_time_start:
            query.setdefault("log_time", {}).update({"$gte": request.log_time_start})

        if request.log_time_end:
            query.setdefault("log_time", {}).update({"$lte": request.log_time_end})

        for field in eq_fields + regex_fields:
            if request_value := getattr(request, field, None):
                if field in eq_fields:
                    query[field] = {"$eq": request_value}

                elif field in regex_fields:
                    query[field] = {"$regex": request_value}

        return query

    @staticmethod
    def get_list_from_file(request: BizLogListRequest) -> ListBizLog:
        """ 从文件中获取业务日志列表 """

        log_list: ListBizLog = []
        all_file_list: ListBizLogFile = BizLogFileGetter.get_biz_log_file(
            include_log_list=True,
            start_time=request.log_time_start,
            end_time=request.log_time_end,
        )

        for log_file in all_file_list:
            log_list += log_file.log_list

        return log_list


if __name__ == "__main__":
    from devtools import debug

    debug(BizLogFileGetter.get_biz_log_file(True))
